package com.tianci.compiler;

import com.google.auto.service.AutoService;
import com.tianci.annotation.Factory;

import java.util.LinkedHashSet;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;

/**https://blog.csdn.net/qq_20521573/article/details/82321755
 * @AutoService注解，它的作用是用来生成META-INF/services/javax.annotation.processing.Processor文件的，也就是我们在使用注解处理器的时候需要手动添加META-INF/services/javax.annotation.processing.Processor，而有了@AutoService后它会自动帮我们生成。AutoService是Google开发的一个库，使用时需要在factory-compiler中添加依赖，如下：
 */

//@AutoService(Process.class)
public class FactoryProcessorNode extends AbstractProcessor {

    /**
     * 这个方法用于初始化处理器，方法中有一个ProcessingEnvironment类型的参数，ProcessingEnvironment是一个注解处理工具的集合。它包含了众多工具类。例如：
     * Filer可以用来编写新文件；
     * Messager可以用来打印错误信息；
     * Elements是一个可以处理Element的工具类。
     *
     * 在这里我们有必要认识一下什么是Element。 在Java语言中，Element是一个接口，表示一个程序元素，它可以指代包、类、方法或者一个变量。Element已知的子接口有如下几种：
     *
     * PackageElement 表示一个包程序元素。提供对有关包及其成员的信息的访问。
     * ExecutableElement 表示某个类或接口的方法、构造方法或初始化程序（静态或实例），包括注释类型元素。
     * TypeElement 表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。注意，枚举类型是一种类，而注解类型是一种接口。
     * VariableElement 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。
     * 接下来，我希望大家先来理解一个新的概念，即抛弃我们现有对Java类的理解，把Java类看作是一个结构化的文件。什么意思？就是把Java类看作一个类似XML或者JSON一样的东西。有了这个概念之后我们就可以很容易的理解什么是Element了。带着这个概念来看下面的代码：
     *package com.zhpan.mannotation.factory;  //    PackageElement
     *
     * public class Circle {  //  TypeElement
     *
     *     private int i; //   VariableElement
     *     private Triangle triangle;  //  VariableElement
     *
     *     public Circle() {} //    ExecuteableElement
     *
     *     public void draw(   //  ExecuteableElement
     *                         String s)   //  VariableElement
     *     {
     *         System.out.println(s);
     *     }
     *
     *     @Override
     *     public void draw() {    //  ExecuteableElement
     *         System.out.println("Draw a circle");
     *     }
     * }
     *
     *
     * @param processingEnv
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        Filer filer = processingEnv.getFiler();
        Messager messager = processingEnv.getMessager();
        Elements elementUtils = processingEnv.getElementUtils();

    }


    /**
     * 终于，到了FactoryProcessor类中最后一个也是最重要的一个方法了。先看这个方法的返回值，是一个boolean类型，返回值表示注解是否由当前Processor 处理。如果返回 true，则这些注解由此注解来处理，后续其它的 Processor 无需再处理它们；如果返回 false，则这些注解未在此Processor中处理并，那么后续 Processor 可以继续处理它们。
     * 在这个方法的方法体中，我们可以校验被注解的对象是否合法、可以编写处理注解的代码，以及自动生成需要的java文件等。因此说这个方法是AbstractProcessor 中的最重要的一个方法。我们要处理的大部分逻辑都是在这个方法中完成。
     *
     * 了解上述四个方法之后我们便可以初步的来编写FactoryProcessor类的代码了，如下：

     * @param annotations
     * @param roundEnv
     * @return
     */

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }

    /**ourceVersion.latestSupported()
     *   * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    /**
     * 这个方法的返回值是一个Set集合，集合中指要处理的注解类型的名称(这里必须是完整的包名+类名，例如com.example.annotation.Factory)。由于在本例中只需要处理@Factory注解，因此Set集合中只需要添加@Factory的名称即可。
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(Factory.class.getCanonicalName());
        return annotations;
    }
}
